#include <iostream>
#include <chrono>

#include "Event.hpp"

#include "Debug.hpp"

Event::~Event()
{
	// nullptr may be passed for events which don't care about using internal data
	if(data)
		data->Destroy();

	endTick = std::chrono::high_resolution_clock::now();
	std::chrono::milliseconds diff = std::chrono::duration_cast<std::chrono::milliseconds>(endTick - startTick);
	if(diff > longest)
	{
		longest = diff;
		std::cout << "Event took " << diff.count() << "ms." << std::endl;
		std::cout << "start " << startTick.time_since_epoch().count() << " end " << endTick.time_since_epoch().count() << std::endl;
	}
}

EventHandler::EventHandler() : ready(false), shutdown(false)
{
	thr = std::thread(&EventHandler::EventThread, this);
}

EventHandler::~EventHandler()
{
	shutdown = true;

	eventsPending.notify_all();

	assert(events.size() == 0);

	thr.join();
}

void EventHandler::Consume()
{
	Event* evt = nullptr;
	std::pair<Listeners::iterator, Listeners::iterator> range;

	while(!shutdown)
	{
		std::unique_lock<std::mutex> lk(mtx);

		while(events.size() == 0)
		{
			eventsPending.wait(lk);
			break;
		}

		while(events.size())
		{
			evt = events.front();
			events.pop();

			range = listeners.equal_range(static_cast<std::underlying_type<EventType>::type>(evt->eventType));
			if(range.first != range.second)
			{
				std::for_each(range.first, range.second, [&](std::pair<std::underlying_type<EventType>::type, std::function<bool(Event*)>> it)
				{
					it.second(evt);
				});
			}

			delete evt;
		}
	}
}

int EventHandler::EventThread()
{
	ready = true;

	Consume();

	return 0;
}

void EventHandler::AddListener(EventType eventType, std::function<bool(Event*)> func)
{
	std::unique_lock<std::mutex> lk(mtx);

	listeners.insert(Listeners::value_type(static_cast<std::underlying_type<EventType>::type>(eventType), func));
}

void EventHandler::RemoveListener(EventType eventType, std::function<bool(Event*)> func)
{
	std::unique_lock<std::mutex> lk(mtx);

	auto range = listeners.equal_range(static_cast<std::underlying_type<EventType>::type>(eventType));
	for(auto it = range.first, end = range.second; it != end; ++it)
	{
		if((*it).second.target<bool(Event*)>() == func.target<bool(Event*)>())
		{
			listeners.erase(it);
			return;
		}
	}
}

std::unique_lock<std::mutex> EventHandler::GetLock()
{
	return std::unique_lock<std::mutex>(mtx);
}
